package sunyu.tools.common;

import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 创建一个项目全局的线程池
 *
 * @author 孙宇
 */
@Component
public class ExecutorServiceTools {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    private ExecutorService executorService = null;

    private int poolSize = Runtime.getRuntime().availableProcessors();

    @PostConstruct
    public void init() {
        logger.info("初始化线程池开始");

        setPoolSize();
        //setPoolSize(1024);

        executorService = Executors.newFixedThreadPool(getPoolSize());

        logger.info("初始化的连接池大小：{}", getPoolSize());

        logger.info("初始化线程池结束");
    }

    /**
     * 根据 Java 虚拟机可用处理器数目返回最佳的线程数。<br>
     * 最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)，其中阻塞系数这里设为0.9
     */
    private void setPoolSize() {
        try {
            // JVM可用处理器的个数
            final int cores = Runtime.getRuntime().availableProcessors();
            logger.info("JVM可用处理器的个数：{}", cores);
            // 最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)
            // 阻塞系数是不是需要有个setter方法能让使用者自由设置呢？
            logger.info("最佳的线程数 = CPU可用核心数 / (1 - 阻塞系数)");
            logger.info("最佳的线程数 = {} / (1 - {})", cores, 0.9);
            poolSize = (int) (cores / (1 - 0.9));
        } catch (Throwable e) {
            logger.error(ExceptionUtils.getStackTrace(e));
        }
    }

    @PreDestroy
    public void destroy() {
        logger.info("销毁所有线程开始");
        if (executorService != null && !executorService.isShutdown()) {
            executorService.shutdown();
        }
        logger.info("销毁所有线程结束");
    }

    public void execute(Runnable runnable) {
        executorService.execute(runnable);
    }

    public Future submit(Callable callback) {
        return executorService.submit(callback);
    }

    public int getPoolSize() {
        return poolSize;
    }

    private void setPoolSize(int poolSize) {
        this.poolSize = poolSize;
    }


    public List waitResults(Collection<Future> futureList) {
        List resultList = new ArrayList();
        waitResults(futureList, resultList);
        return resultList;
    }

    public void waitResults(Collection<Future> futureList,
                            Collection resultList) {
        for (Future f : futureList) {
            try {
                resultList.add(f.get());
            } catch (InterruptedException e) {
                logger.error(ExceptionUtils.getStackTrace(e));
            } catch (ExecutionException e) {
                logger.error(ExceptionUtils.getStackTrace(e));
            }
        }
    }

}
